const db = require('../db');
const config = require('../../config');
const axios = require('axios');
const tool = require('../tools/tool');
// const departments = require('./departments');
// const permissions = require('./permissions');
// const settings = require('./settings')
// const vcode = require('./vcode');
const runner = require('../runner')

const exp = {
    getOpenId(code) {
        return new Promise(async (resolve, reject) => {
            const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${config.appId}&secret=${config.appSecret}&js_code=${code}&grant_type=authorization_code`
            // console.log(url)
            try {
                const response = await axios.get(url)
                const { openid, session_key, errcode, errmsg } = response.data
                if (errcode) {
                    // console.log(errcode, errmsg)
                    reject(`获取 openId 失败: ${errmsg}`)
                }
                return resolve(openid);
            } catch (error) {
                reject(error)
            }
        })
    },
    async checkToken(token, admin, per1, per2, sadmin) {
        let token_c = token
        if (!token) throw '未登录'
        if (token == '[object Undefined]') throw '未登录'
        token = tool.decodeString(token)
        token = tool.JSON.parse(token)
        let userInfo = await this.getSingleUserInfoByOpenId(token.openId)
        if (!userInfo) throw '未登录'
        if (new Date() - new Date(token.time) > 1000 * 60 * 60 * 24) throw '长时间未活动，请重新登录'
        if (userInfo.id !== token.id) throw '已登录其他账号'
        if (userInfo.token != token_c) throw '已在其他位置登录'
        if (userInfo.isDisabled === 1) throw '账号被封禁'
        if (userInfo.isDisabled === 2) throw '还没有通过审核'
        if (admin && !userInfo.isAdmin) throw '需要管理员权限'
        if (sadmin && !userInfo.isSAdmin) throw '需要超级管理员权限'
        if (per1 && !(await tool.methods.permissions().getUserPermissionsListType1(userInfo.id)).length && !userInfo.isAdmin) throw '需要负责人权限'
        if (per2 && !(await tool.methods.permissions().getUserPermissionsListType2(userInfo.id)).length && !userInfo.isAdmin) throw '需要审核人权限'
    },
    async format(t, withOutKeyInfo = false) {
        if (!t) return
        t.departmentInfo = await tool.methods.departments().getInfo(t.departmentId)
        if (withOutKeyInfo) {
            delete t.token
            delete t.password
        }
        t.permissions = {
            handle: await tool.methods.permissions().getUserPermissionsListType1(t.id),
            review: await tool.methods.permissions().getUserPermissionsListType2(t.id),
            admin: t.isAdmin
        }
        if (t.patrolCount < 0) t.patrolCount = 0
        t.patrol = {
            create: (await db.exeWithParams(`select count(*) as total from patrols where userId = ?`, [t.id]))[0].total,
            handle: (await db.exeWithParams(`select count(distinct patrolId) as total from patrols_handle where userId = ?`, [t.id]))[0].total,
            review: (await db.exeWithParams(`select count(distinct patrolId) as total from patrols_review where userId = ?`, [t.id]))[0].total,
            remain: ((await tool.methods.settings().getItem('dailyPatrolLimitPerUser')) - (tool.formatDateString(new Date(t.lastPatrolCreatedAt)) == tool.formatDateString(new Date()) ? t.patrolCount : 0))
        }
        return t
    },
    async getSingleUserInfoByOpenId(openId, includeDeleted = false, withOutKeyInfo = false) {
        let t = (await db.select(`select * from users where openId = '${openId}'`, {}, includeDeleted))[0]
        if (!t) return
        let re = await this.format(t)
        if (runner.currentData.logined === undefined) {
            re.username = `${re.username.slice(0, 1)}**`
        }
        return re
    },
    async getSingleUserInfoByAccount(account, includeDeleted = false, withOutKeyInfo = false) {
        let t = (await db.select(`select * from users where account = '${account}'`, {}, includeDeleted))[0]
        if (!t) return
        let re = await this.format(t)
        if (runner.currentData.logined === undefined) {
            re.username = `${re.username.slice(0, 1)}**`
        }
        return re
    },
    async getSingleUserInfoByPhone(phone, includeDeleted = false, withOutKeyInfo = false) {
        let t = (await db.select(`select * from users where phone = '${phone}'`, {}, includeDeleted))[0]
        if (!t) return
        let re = await this.format(t)
        if (runner.currentData.logined === undefined) {
            re.username = `${re.username.slice(0, 1)}**`
        }
        return re
    },
    async getSingleUserInfoByUserId(userId, includeDeleted = false, withOutKeyInfo = false) {
        let t = (await db.select(`select * from users where id = ${userId}`, {}, includeDeleted))[0]
        if (!t) return
        let re = await this.format(t)
        if (runner.currentData.logined === undefined) {
            re.username = `${re.username.slice(0, 1)}**`
        }
        return re
    },
    async genToken(openId, id) {
        return tool.encodeString(JSON.stringify({
            openId,
            time: tool.formatDate3(new Date()),
            id
        }))
    },
    async updateToken(userId, newToken) {
        await db.updateById('users', userId, {
            token: newToken
        })
    },
    async addNew(openId, info) {
        if ((await db.select(`select * from users where phone = '${info.phone}'`)).length) throw '手机号已存在'
        if ((await db.select(`select * from users where account = '${info.account}'`)).length) throw '账号（学号/工号）已存在'
        let id = await db.insertInto('users', Object.assign(info, {
            openId,
            isDeleted: 0,
            isDisabled: ((await tool.methods.settings().getItem('accountVer')) ? 2 : 0),
            isAdmin: 0,
            limitNumOfPatrol: 1
        }))
        return id
    },
    async getDepartmentList() {
        return await db.select(`select * from departments`)
    },
    async checkUserPassword(data) {
        let re = await this.getSingleUserInfoByAccount(data.param.form.account)
        if (!re) throw '用户不存在'
        if (re.password != data.param.form.password) throw '密码错误'
    },
    async setOpenIdByPhone(phone, openId) {
        let re = await this.getSingleUserInfoByPhone(phone)
        if (!re) throw '用户不存在'
        await db.updateById('users', re.id, {
            openId
        })
    },
    async setOpenIdByAccount(account, openId) {
        let re = await this.getSingleUserInfoByAccount(account)
        if (!re) throw '用户不存在'
        await db.updateById('users', re.id, {
            openId
        })
    },
    async updateInfo(userId, update = {}) {
        await db.updateById('users', userId, update)
    },
    async exitLogin(userId) {
        await db.updateById('users', userId, {
            openId: ''
        })
    },
    update: {
        async account(userId, account) {
            let re = await exp.getSingleUserInfoByUserId(userId)
            if (!re) throw '用户不存在'
            if ((await db.selectWithParams(`select * from users where account = ? and id != ?`, [account, userId])).length) throw '此账号（学号、工号）已被占用'
            await exp.updateInfo(userId, {
                account
            })
        },
        async phone(userId, vcodeStr, phone) {
            let re = await exp.getSingleUserInfoByUserId(userId)
            if (!re) throw '用户不存在'
            if (!await tool.methods.vcode().check(vcodeStr, phone)) throw '验证码错误或已过期'
            if ((await db.selectWithParams(`select * from users where phone = ? and id != ?`, [phone, userId])).length) throw '此手机号已被占用'
            await exp.updateInfo(userId, {
                phone
            })
        },
        async admin(userId, value) {
            let re = await exp.getSingleUserInfoByUserId(userId)
            if (!re) throw '用户不存在'
            if (re.isSAdmin && !value) throw '超级管理员需要具有管理员权限'
            await exp.updateInfo(userId, {
                isAdmin: value
            })
        },
        async phone_noVCode(userId, phone) {
            let re = await exp.getSingleUserInfoByUserId(userId)
            if (!re) throw '用户不存在'
            if ((await db.selectWithParams(`select * from users where phone = ? and id != ?`, [phone, userId])).length) throw '此手机号已被占用'
            await exp.updateInfo(userId, {
                phone
            })
        },
    },
    async setUnlogin(userId) {
        await db.updateById('users', userId, {
            openId: ''
        })
    },
    async getMyTasks(userId) {
        const patrol = require('./patrol')
        let userData = await this.getSingleUserInfoByUserId(userId)
        if (!userData) throw '用户不存在'
        let campuses = await db.select(`select * from campuses`)
        let categories = await db.select(`select * from categories`)
        let waitingForHandle = []
        for (let i = 0; i < campuses.length; i++) {
            for (let i1 = 0; i1 < categories.length; i1++) {
                let t1 = await patrol.get.userPatrolNum(userId, campuses[i].id, categories[i1].id, 1)
                // if (t1) waitingForHandle[`${campuses[i].name}-${categories[i1].name}`] = t1
                if (t1) waitingForHandle.push({
                    name: `${campuses[i].name}-${categories[i1].name}`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t1
                })
                let t2 = await patrol.get.userPatrolNum(userId, campuses[i].id, categories[i1].id, 2)
                if (t2) waitingForHandle.push({
                    name: `${campuses[i].name}-${categories[i1].name}（延期）`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t2,
                    isDelay: true
                })
            }
        }
        let waitingForRating = []
        for (let i = 0; i < campuses.length; i++) {
            for (let i1 = 0; i1 < categories.length; i1++) {
                let t1 = await patrol.get.userPatrolNum(userId, campuses[i].id, categories[i1].id, 3)
                if (t1) waitingForRating.push({
                    name: `${campuses[i].name}-${categories[i1].name}`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t1
                })
            }
        }
        let preToHandle = []
        for (let i = 0; i < campuses.length; i++) {
            for (let i1 = 0; i1 < categories.length; i1++) {
                if (!(await tool.methods.permissions().checkUserPermission(userId, 1, campuses[i].id, categories[i1].id, true))) continue
                let t1 = await patrol.get.userPreToHandlePatrolNum(userId, campuses[i].id, categories[i1].id, false)
                preToHandle.push({
                    name: `${campuses[i].name}-${categories[i1].name}`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t1
                })
                let t2 = await patrol.get.userPreToHandlePatrolNum(userId, campuses[i].id, categories[i1].id, true)
                if (t2) preToHandle.push({
                    name: `${campuses[i].name}-${categories[i1].name}（延期）`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t2,
                    isDelay: true
                })
            }
        }
        let preToReview = []
        for (let i = 0; i < campuses.length; i++) {
            for (let i1 = 0; i1 < categories.length; i1++) {
                if (!(await tool.methods.permissions().checkUserPermission(userId, 2, campuses[i].id, categories[i1].id, true))) continue
                let t1 = await patrol.get.userPreToReviewPatrolNum(userId, campuses[i].id, categories[i1].id)
                preToReview.push({
                    name: `${campuses[i].name}-${categories[i1].name}`,
                    campusInfo: campuses[i],
                    categoryInfo: categories[i1],
                    num: t1
                })
            }
        }
        // console.log(waitingForHandle,
        //     waitingForRating,
        //     preToHandle,
        //     preToReview)
        let ret = {
            waitingForHandle,
            waitingForRating,
            preToHandle,
            preToReview,
            admin: await (async () => {
                if (userData.isAdmin) {
                    return [
                        {
                            name: '管理全部',
                            num: (await db.select(`select count(*) as total from patrols where status > 0`))[0].total
                        }
                    ]
                } else {
                    return []
                }
            })()
        }
        let isBlank = true
        await tool.object.for(tool.deepCopy(ret), (k, v) => {
            if (!(!ret[k] || !ret[k].length)) {
                isBlank = false
            }
        })
        ret.isBlank = isBlank
        return ret
    },
    admin: {
        async getList(page = 1, size = 10, account = '', username = '', phone = '', role = '', isAdmin = undefined, departmentId = undefined, isDisabled = undefined) {
            let re = await db.selectWithParams(`select * from users where account like ? and username like ? and phone like ? and role like ?  ${isAdmin === undefined ? `` : ` and isAdmin = ? `} ${departmentId === undefined ? '' : ' and departmentId = ? '} ${isDisabled === undefined ? '' : ` and isDisabled = ? `} order by id desc limit ?,? `, [`%${account}%`, `%${username}%`, `%${phone}%`, `%${role}%`,
            ...(() => {
                if (isAdmin !== undefined) {
                    return [isAdmin]
                } else {
                    return []
                }
            })(),
            ...(() => {
                if (departmentId !== undefined) {
                    return [departmentId]
                } else {
                    return []
                }
            })(),
            ...(() => {
                if (isDisabled !== undefined) {
                    return [isDisabled]
                } else {
                    return []
                }
            })(),
            (page - 1) * size,
                size
            ])
            await tool.array.format(re, (item) => {
                return exp.format(item)
            })
            return {
                list: re,
                total: (await db.selectWithParams(`select count(*) as total from users where account like ? and username like ? and phone like ? and role like ? ${isAdmin === undefined ? `` : ` and isAdmin = ? `} ${departmentId === undefined ? '' : ' and departmentId = ? '}  ${isDisabled === undefined ? '' : ` and isDisabled = ? `}`, [`%${account}%`, `%${username}%`, `%${phone}%`, `%${role}%`,
                ...(() => {
                    if (isAdmin !== undefined) {
                        return [isAdmin]
                    } else {
                        return []
                    }
                })(),
                ...(() => {
                    if (departmentId !== undefined) {
                        return [departmentId]
                    } else {
                        return []
                    }
                })(),
                ...(() => {
                    if (isDisabled !== undefined) {
                        return [isDisabled]
                    } else {
                        return []
                    }
                })(),
                ]))[0].total
            }
        },
        async update(userId, values) {
            await db.updateById('users', userId, values)
        },
        async deleteList(arr = []) {
            for (let i = 0; i < arr.length; i++) {
                let userData = await exp.getSingleUserInfoByUserId(arr[i])
                if (!userData) continue
                if (userData.isSAdmin) throw '不能删除超级管理员'
            }
            for (let i = 0; i < arr.length; i++) {
                await tool.methods.permissions().deleteUserAllPers(arr[i])
                await db.deleteById('users', arr[i])
            }
        },
        async getListByCampusCategory(campusId, categoryId) {
            let re1 = await db.selectWithParams(`select * from permissions where campusId = ? and categoryId = ? and permissions.type = 1`, [campusId, categoryId])
            for (let i = 0; i < re1.length; i++)re1[i] = {
                ...await exp.getSingleUserInfoByUserId(re1[i].userId),
                perId: re1[i].id
            }
            let re2 = await db.selectWithParams(`select * from permissions where campusId = ? and categoryId = ? and permissions.type = 2`, [campusId, categoryId])
            for (let i = 0; i < re2.length; i++)re2[i] = {
                ...await exp.getSingleUserInfoByUserId(re2[i].userId),
                perId: re2[i].id
            }
            return {
                handle: re1.filter(item => item !== undefined),
                review: re2.filter(item => item !== undefined)
            }
        },
        async exportToExcel(userId) {
            let re = await db.select(`select filtered_table.*, departments.name as departmentName from users, departments where filtered_table.departmentId = departments.id`)
            for (let i = 0; i < re.length; i++) {
                delete re[i].id
                delete re[i].password
                delete re[i].createdAt
                delete re[i].updatedAt
                delete re[i].isDeleted
                delete re[i].isDisabled
                delete re[i].token
                delete re[i].patrolCount
                delete re[i].lastPatrolCreatedAt
            }
            let workbook = await tool.exportDataToExcel(re, [
                { name: "username", label: "用户名", type: "text" },
                { name: "phone", label: "手机号", type: "text" },
                { name: "role", label: "角色", type: "text" },
                { name: "openId", label: "OpenID", type: "text" },
                { name: "isAdmin", label: "是否管理员", type: "text" },
                { name: "account", label: "账号", type: "text" },
                { name: "isSAdmin", label: "是否超级管理员", type: "text" },
                { name: "departmentName", label: "部门名称", type: "text" }
            ], () => { })
            let fileName = await tool.saveWorkbookToFile(workbook)
            await tool.methods.alert().genExcel(userId, fileName)
        }
    }
}

module.exports = exp